﻿#region COPYRIGHT© 2009-2013 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Dynamic;

namespace FlitBit.Json
{
	public static class JDynamic
	{
		public static dynamic Parse(string text)
		{
			Contract.Requires<ArgumentNullException>(text != null);
			Contract.Requires<ArgumentException>(text.Length > 0);

			var val = JsonValue.Parse(text);

			dynamic result = null;
			switch (val.Kind)
			{
				case JsonNodeKind.Object:
					result = ToDynamic((JsonObject) val);
					break;
				case JsonNodeKind.Array:
					result = ToDynamic((JsonArray) val);
					break;
				case JsonNodeKind.StringLiteral:
					var s = val as JsonStringLiteral;
					Contract.Assume(((string)null) != s);
					if (s.DateKind == JsonDateConstantKind.None)
					{
						result = (string) val;
					}
					else
					{
						result = s.ToDateTime(null);
					}
					break;
				case JsonNodeKind.NumericLiteral:
					result = val.ToDecimal(null);
					break;
				case JsonNodeKind.BooleanLiteral:
					result = val.ToBoolean(null);
					break;
			}
			return result;
		}

		public static object ToDynamic(JsonObject jobj)
		{
			var expando = new ExpandoObject() as IDictionary<string, object>;
			foreach (var member in jobj)
			{
				switch (member.Value.Kind)
				{
					case JsonNodeKind.Object:
						expando.Add(member.Name, ToDynamic((JsonObject) member.Value));
						break;
					case JsonNodeKind.Array:
						expando.Add(member.Name, ToDynamic((JsonArray) member.Value));
						break;
					case JsonNodeKind.StringLiteral:
						var s = member.Value as JsonStringLiteral;
						Contract.Assume(((string)null) != s);
						if (s.DateKind == JsonDateConstantKind.None)
						{
							expando.Add(member.Name, (string) member.Value);
						}
						else
						{
							expando.Add(member.Name, s.ToDateTime(null));
						}
						break;
					case JsonNodeKind.NumericLiteral:
						expando.Add(member.Name, member.Value.ToDecimal(null));
						break;
					case JsonNodeKind.BooleanLiteral:
						expando.Add(member.Name, member.Value.ToBoolean(null));
						break;
					case JsonNodeKind.Null:
						expando.Add(member.Name, null);
						break;
				}
			}
			return expando;
		}

		public static object ToDynamic(JsonArray jarr)
		{
			dynamic array = new List<dynamic>();
			foreach (var val in jarr)
			{
				switch (val.Kind)
				{
					case JsonNodeKind.Object:
						array.Add(ToDynamic((JsonObject) val));
						break;
					case JsonNodeKind.Array:
						array.Add(ToDynamic((JsonArray) val));
						break;
					case JsonNodeKind.StringLiteral:
						var s = val as JsonStringLiteral;
						Contract.Assume(((string)null) != s);
						if (s.DateKind == JsonDateConstantKind.None)
						{
							array.Add((string) val);
						}
						else
						{
							array.Add(s.ToDateTime(null));
						}
						break;
					case JsonNodeKind.NumericLiteral:
						array.Add(val.ToDecimal(null));
						break;
					case JsonNodeKind.BooleanLiteral:
						array.Add(val.ToBoolean(null));
						break;
					case JsonNodeKind.Null:
						array.Add(null);
						break;
				}
			}
			return array;
		}
	}
}